module neo.tetris.view {
	/**
	 *
	 * @author 
	 *
	 */
	export class TetrisViewBoard extends eui.Component {
        static COLORS:number[] = [
            neo.typescript.Color.RGB(0,0,0),
            neo.typescript.Color.RGB(204,102,102),
            neo.typescript.Color.RGB(102,204,102),
            neo.typescript.Color.RGB(102,102,204),
            neo.typescript.Color.RGB(204,204,102),
            neo.typescript.Color.RGB(204,102,204),
            neo.typescript.Color.RGB(102,204,204),
            neo.typescript.Color.RGB(218,170,0)
        ];
        
        public battleView: neo.tetris.view.BattleView;
        
        isStarted: boolean = false;
        curX: number = 0;
        curY: number = 0;
        
        dropDownTimer: egret.Timer;
        synchronizeTimer: egret.Timer;

        background: egret.Sprite;
        foreground: egret.Sprite;
        _battlePlayer: neo.tetris.model.BattlePlayerModel;
        set battlePlayer(value: neo.tetris.model.BattlePlayerModel) {
            this._battlePlayer = value;
            this._battlePlayer.addEventListener(neo.typescript.model.Bean.PROPERTY_CHANGE,this.handlePlayerEvt,this);
            this._battlePlayer.battle.addEventListener(neo.typescript.model.Bean.PROPERTY_CHANGE,this.handleBattleChanged,this);
            this.tetrisModel = this._battlePlayer.tetris;
            this.tetrisModel.addEventListener(neo.typescript.model.Bean.PROPERTY_CHANGE,this.handleTetrisChanged,this);
            this.handleTetrisChanged();
            this.handleBattleChanged();
        }
        

        private handlePlayerEvt(evt: egret.Event = null): void {
            if(!evt || evt.data == neo.tetris.model.BattlePlayerModel.ADD_DIFFICULT_TIME) {
                this.battleView.difficultTo(this,this == this.battleView.mainBoard ? this.battleView.otherPlayerBoard0 : this.battleView.mainBoard);
            }
        }


        tetrisModel: neo.tetris.model.TetrisModel;
    	
		public constructor() {
            super();
            
            this.addEventListener(egret.Event.ADDED_TO_STAGE,this.handleEvent,this);
            this.addEventListener(egret.Event.REMOVED_FROM_STAGE,this.handleEvent,this);
		}

        private handleEvent(evt: egret.Event): void {
            switch(evt.type) {
                case egret.Event.ADDED_TO_STAGE:
                    // initialize background
                    this.background = new egret.Sprite();
                    this.addChild(this.background);
                    this.background.graphics.beginFill(0x2f2f2f);
                    this.background.graphics.drawRect(0,0,this.width,this.height);
                    this.background.graphics.endFill();

                    for(r = 0;r < neo.tetris.model.TetrisModel.row;r++) {
                        for(c = 0;c < neo.tetris.model.TetrisModel.column;c++) {
                            if (r % 2 == 0)
                                this.background.graphics.beginFill(c % 2 == 0 ? 0x2f2f2f : 0x2b2b2b);
                            else
                                this.background.graphics.beginFill(c % 2 == 1 ? 0x2f2f2f : 0x2b2b2b);
                            this.background.graphics.drawRect(Math.floor(c * this.squareWidth),Math.floor(r * this.squareHeight),Math.floor(this.squareWidth),Math.floor(this.squareHeight));
                            this.background.graphics.endFill();
                        }
                    }
                    
                    this.background.graphics.lineStyle(2,0x222222);
                    for(var r: number = 0;r < neo.tetris.model.TetrisModel.row; r++) {
                        this.background.graphics.moveTo(0,Math.floor(this.squareHeight * r));
                        this.background.graphics.lineTo(this.width,Math.floor(this.squareHeight * r));
                        this.background.graphics.endFill();
                    }
                    for(var c: number = 0;c < neo.tetris.model.TetrisModel.column;c++) {
                        this.background.graphics.moveTo(Math.floor(this.squareWidth * c),0);
                        this.background.graphics.lineTo(Math.floor(this.squareWidth * c),this.height);
                        this.background.graphics.endFill();
                    }
                    
                    // initialize foreground
                    this.foreground = new egret.Sprite();
                    this.addChild(this.foreground);

                    this.dropDownTimer = new egret.Timer(400);
                    this.dropDownTimer.addEventListener(egret.TimerEvent.TIMER,this.handleTimerEvent,this);
                    this.synchronizeTimer = new egret.Timer(500);
                    this.synchronizeTimer.addEventListener(egret.TimerEvent.TIMER,this.handleTimerEvent,this);
                    break;
                case egret.Event.REMOVED_FROM_STAGE:
                    this.dropDownTimer.stop();
                    break;
            }
        }
        
        private handleBattleChanged(evt:egret.Event = null):void {
            if (!evt || evt.data == neo.tetris.model.BattleModel.START_TIME) {
                if(isNaN(this._battlePlayer.battle.startTime)) {
                    this.dropDownTimer.stop();
                } else {
                    if (this._battlePlayer.userUUid == neo.tetris.model.appInstance.logined.uuid)
                        this.restart();
                }
            }
            if (!evt || evt.data == neo.tetris.model.BattleModel.WINNER) {
                if(this._battlePlayer.battle.winner) {
                    this.dropDownTimer.stop();
                    // show winner
                    
                    // then rematch
                    
                }
            }
        }
        
        private handleTetrisChanged(evt:egret.Event = null):void {
            if(this._battlePlayer && this._battlePlayer.userUUid == neo.tetris.model.appInstance.logined.uuid) {
                // start timer
                this.dropDownTimer.start();
                this.synchronizeTimer.start();

                // handle touch
                this.touchEnabled = true;
                this.addEventListener(egret.TouchEvent.TOUCH_BEGIN,this.handleTouchEvent,this);
                this.addEventListener(egret.TouchEvent.TOUCH_MOVE,this.handleTouchEvent,this);
                this.addEventListener(egret.TouchEvent.TOUCH_END,this.handleTouchEvent,this);
            } else {
                this.dropDownTimer.stop();
                this.synchronizeTimer.stop();
                
                this.touchEnabled = false;
                this.removeEventListener(egret.TouchEvent.TOUCH_BEGIN,this.handleTouchEvent,this);
                this.removeEventListener(egret.TouchEvent.TOUCH_MOVE,this.handleTouchEvent,this);
                this.removeEventListener(egret.TouchEvent.TOUCH_END,this.handleTouchEvent,this);
            }

            this.repaint();
        }

		private handleTimerEvent(evt:egret.TimerEvent):void {
    		switch (evt.target) {
                case this.dropDownTimer:
                    if(this._battlePlayer && neo.tetris.model.appInstance.serverTime > this._battlePlayer.battle.startTime)
                        this.dropDownByStep();
                    break;
                case this.synchronizeTimer:
                    var code:Array<number> = new Array<number>();
                    for(var i: number = 0;i < this.tetrisModel.shapes.length; i++) {
                        var shape:neo.tetris.model.ShapeModel = this.tetrisModel.shapes[i];
                        if(shape) {
                            code.push(i);
                            code.push(shape.catalog);
                        }
                    }
                    Main.instance.battleService.stateSynchronize(code.join("|"));
                    break;
    		}
		}
		
        private lastTouch:number[];
        private handleTouchEvent(evt:egret.TouchEvent):void {
            switch (evt.type) {
                case egret.TouchEvent.TOUCH_TAP:
                    return;
                    break;
                case egret.TouchEvent.TOUCH_BEGIN:
                    this.lastTouch = [evt.stageX,evt.stageY];
                    break;
                case egret.TouchEvent.TOUCH_MOVE:
                    if (this.tetrisModel.curPiece) {
                        if(this.lastTouch) {
                            var radius: number = Math.atan2(evt.stageY - this.lastTouch[1],evt.stageX - this.lastTouch[0]);
                            if(Math.abs(radius) < Math.PI * .25) {
                                this.tryMove(this.tetrisModel.curPiece,this.curX + 1,this.curY);
                                this.lastTouch = null;
                            } else if(Math.PI * .75 < Math.abs(radius)) {
                                this.tryMove(this.tetrisModel.curPiece,this.curX - 1,this.curY);
                                this.lastTouch = null;
                            } else if(Math.PI * .25 < radius && radius < Math.PI * .75) {
                                this.dropDown();
                                this.lastTouch = null;
                            } else {
                                this.tryMove(this.tetrisModel.curPiece.rotateRight(),this.curX,this.curY);
                                this.lastTouch = null;
                            }
                        }
                    }
                    break;
                case egret.TouchEvent.TOUCH_END:
                    break;
            }
        }
        
        get squareWidth():number {
            return this.width / neo.tetris.model.TetrisModel.column;
        }
        
        get squareHeight():number {
            return this.height / neo.tetris.model.TetrisModel.row;
        }
        
        shapeAt(x: number,y: number): neo.tetris.model.ShapeModel {
            return this.tetrisModel.shapes[(y * neo.tetris.model.TetrisModel.column) + x]
        }
        
        public restart():void {
            this.isStarted = true;
            this.tetrisModel.numLinesRemoved = 0;
            this.clearBoard();
            this.newPiece();
            this.dropDownTimer.start();
        }
        
        private repaint():void {
            this.paint();
        }
        
        public paint():void {
            this.foreground.graphics.clear();
            this.foreground.graphics.beginFill(0xffffff,.1);
            this.foreground.graphics.drawRect(0,0,this.width,this.height);
            this.foreground.graphics.endFill();
            var boardTop: number = this.height - neo.tetris.model.TetrisModel.row * this.squareHeight;
            for(var i: number = 0;i < neo.tetris.model.TetrisModel.row; ++i) {
                for(var j: number = 0;j < neo.tetris.model.TetrisModel.column; ++j) {
                    var shape:neo.tetris.model.ShapeModel = this.shapeAt(j,neo.tetris.model.TetrisModel.row - i - 1);
                    if (shape) {
                        this.drawSquare(this.foreground.graphics,0 + j * this.squareWidth,boardTop + i * this.squareHeight,shape);
                    }
                }
            }
            if(this.tetrisModel.curPiece) {
                for(var i: number = 0;i < 4;++i) {
                    var x: number = this.curX + this.tetrisModel.curPiece.getX(i);
                    var y: number = this.curY - this.tetrisModel.curPiece.getY(i);
                    this.drawSquare(this.foreground.graphics,0 + x * this.squareWidth,boardTop + (neo.tetris.model.TetrisModel.row - y - 1) * this.squareHeight,this.tetrisModel.curPiece);
                }
            }
        }
        
        private dropDown():void {
            var newY: number = this.curY;
            while (newY > 0) {
                if(!this.tryMove(this.tetrisModel.curPiece,this.curX,newY - 1))
                    break;
                --newY;
            }
            this.pieceDropped(this.curX, this.curY);
        }
        
        private dropDownByStep():void {
            if(!this.tryMove(this.tetrisModel.curPiece,this.curX,this.curY - 1))
                this.pieceDropped(this.curX, this.curY);
        }
        
        private clearBoard():void {
            for(var i: number = 0;i < neo.tetris.model.TetrisModel.row * neo.tetris.model.TetrisModel.column; ++i) {
                this.tetrisModel.shapes[i] = null;
            }
        }
        
        private pieceDropped(curX:number, curY:number):void {
            // only handle my shapes
            if (this._battlePlayer.userUUid == neo.tetris.model.appInstance.logined.uuid) {
                for(var i: number = 0;i < 4;++i) {
                    var x: number = curX + this.tetrisModel.curPiece.getX(i);
                    var y: number = curY - this.tetrisModel.curPiece.getY(i);
                    this.tetrisModel.shapes[y * neo.tetris.model.TetrisModel.column + x] = this.tetrisModel.curPiece;
                }
                this.removeFullLines();
                this.newPiece();
            }
        }

        private removeFullLines(): void {
            var numFullLines: number = 0;

            for(var i: number = neo.tetris.model.TetrisModel.row - 1;i >= 0;--i) {
                var lineIsFull: boolean = true;

                for(var j: number = 0;j < neo.tetris.model.TetrisModel.column;++j) {
                    if(this.shapeAt(j,i) == null) {
                        lineIsFull = false;
                        break;
                    }
                }

                if(lineIsFull) {
                    ++numFullLines;
                    for(var k = i;k < neo.tetris.model.TetrisModel.row - 1;++k) {
                        for(var j: number = 0;j < neo.tetris.model.TetrisModel.column;++j) {
                            this.tetrisModel.shapes[(k * neo.tetris.model.TetrisModel.column) + j] = this.shapeAt(j,k + 1);
                        }
                    }
                    Main.instance.battleService.addDifficult(1);
                }
            }

            if(numFullLines > 0) {
                this.tetrisModel.curPiece = null;
            }
        }
        
        private newPiece():void {
            this.tetrisModel.curPiece = neo.tetris.model.ALL[Math.floor(Math.random() * (neo.tetris.model.ALL.length))];
            this.curX = neo.tetris.model.TetrisModel.column / 2 + 1;
            this.curY = neo.tetris.model.TetrisModel.row - 1 + this.tetrisModel.curPiece.minY();
            
            if(!this.tryMove(this.tetrisModel.curPiece, this.curX, this.curY)) {
                this.tetrisModel.curPiece = null;
                this.dropDownTimer.stop();
                this.isStarted = false;
                Main.instance.battleService.surrender();
            }
        }
        
        private tryMove(newPiece: neo.tetris.model.ShapeModel, newX:number, newY:number): boolean {
            for(var i:number = 0;i < 4; ++i) {
                var x:number = newX + newPiece.getX(i);
                var y:number = newY - newPiece.getY(i);
                if(x < 0 || x >= neo.tetris.model.TetrisModel.column || y < 0 || y >= neo.tetris.model.TetrisModel.row)
                    return false;
                if(this.shapeAt(x,y) != null)
                    return false;
            }
    
            this.tetrisModel.curPiece = newPiece;
            this.curX = newX;
            this.curY = newY;
            this.repaint();
            return true;
        }

        private drawSquare(g: egret.Graphics,x: number,y: number,shape: neo.tetris.model.ShapeModel):void {
            x = Math.floor(x);
            y = Math.floor(y);
            var color: number = TetrisViewBoard.COLORS[shape.catalog];
            
            g.beginFill(color);
            g.drawRect(x + 1,y + 1,Math.floor(this.squareWidth - 2),Math.floor(this.squareHeight - 2));
            g.endFill();
            return;
            g.lineStyle(1, neo.typescript.Color.adjustBrightnessByValue(color, 10));
            g.moveTo(x,y + this.squareHeight - 1);
            g.lineTo(x,y);
            g.endFill();
            g.moveTo(x,y);
            g.lineTo(x + this.squareWidth - 1,y);
            g.endFill();
            
            g.lineStyle(1, neo.typescript.Color.adjustBrightnessByValue(color, -10));
            g.moveTo(x + 1,y + this.squareHeight - 1);
            g.lineTo(x + this.squareWidth - 1,y + this.squareHeight - 1);
            g.endFill();
            g.moveTo(x + this.squareWidth - 1,y + this.squareHeight - 1);
            g.lineTo(x + this.squareWidth - 1,y + 1);
            g.endFill();
        }
	}
}
